from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect
from django.contrib.auth.models import User
from django.contrib.auth.views import logout as django_logout
from django.contrib.auth.decorators import user_passes_test
from django.views.generic.simple import redirect_to
from django.core.urlresolvers import reverse
from django.core.paginator import ObjectPaginator, InvalidPage
from django.core.cache import cache
from django.db.models import signals
from django.dispatch import dispatcher
from django.template import RequestContext
from blogs.models import Blog, Post, Tag, Link, Comment, Banned, Image
from blogs.forms import AddPostForm, EditPostForm, BlogDetailsForm, CommentForm, LinkForm, ImageForm
from django.conf import settings
from blogs.utils import get_response_dict

def staff_required(function=None):
    """
    Decorator for views that checks that the user is staff.
    """
    actual_decorator = user_passes_test(lambda u: u.is_staff)
    if function:
        return actual_decorator(function)
    return actual_decorator 
    
# Cleans up the post body to strip out invalid Word characters
def clean_body(body):
    
    replace_map = {
        '&rsquo;': "'",
        '&lsquo;': "'",
        '&ldquo;': '"',
        '&rdquo;': '"',
        '&hellip;': '...',
        '&ndash;': '-',
        '&mdash;': '-',
    }
    
    for k, v in replace_map.iteritems():
        body = body.replace(k, v)
    
    return body
    
    
def admin_reponse_direct(*arguments, **kwargs):
    response_dict = get_response_dict(*arguments, **kwargs)
    return response_dict
    

def logout(request):
    return django_logout(request)


@staff_required
def index(request):

    blogs = None
    user = request.user
    
    if user.is_superuser:
        blogs = Blog.objects.all()
    else:
        blogs = Blog.objects.filter(users__id__exact=request.user.id)

    return render_to_response('blogs/admin/index.html', {
        'user': request.user, 
        'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
        'blogs': blogs,
    }, context_instance=RequestContext(request))


@staff_required
def dashboard(request, blog_slug):
    
    blog = get_object_or_404(Blog, pk=blog_slug)
    
    page_num = request.GET.get('page')
    
    if page_num == None:
        page_num = 1
    else:
        page_num = int(page_num)
    
    paginator = ObjectPaginator(blog.get_posts(), settings.BLOG_ADMIN_NUM_ITEMS)    
        
    return render_to_response('blogs/admin/dashboard.html', admin_reponse_direct(blog, request.user, {
        'user': request.user, 
        'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
        'blog': blog,
        'published': paginator.get_page(page_num-1),
        'has_previous_page': paginator.has_previous_page(page_num-1),
        'has_next_page': paginator.has_next_page(page_num-1),
        'prev_page': page_num-1,
        'next_page': page_num+1,
        'drafts': blog.get_posts(drafts=True, order_by='-mod_date'),
    }), context_instance=RequestContext(request))
    
    
@staff_required
def post_add(request, blog_slug):

    blog = get_object_or_404(Blog, pk=blog_slug)

    if request.method == 'POST':
        
        # Whether user wants to continue editing the post or not
        continue_edit = not request.POST.get('_continue') == None
        
        # Whether user wants to save draft or publish
        publish = not continue_edit and request.POST.get('_save') == None
        
        # Grab the form
        form = AddPostForm(request.POST)
        
        # Check if form is valid, if so then save it
        if form.is_valid():
            
            # Start: Save post
            
            from datetime import datetime
            
            post = Post(
                blog = blog,
                slug = request.POST['slug'],
                title = request.POST['title'],
                byline = request.POST['byline'] if not request.POST['byline'] == '' else request.user.username,
                mod_user = request.user.username,
                body = clean_body(request.POST['body']),
                comments_enabled = request.POST.get('comments_enabled') == 'on',
                mod_date = datetime.now()
            )
            
            if publish:
                post.pub_date = post.mod_date
                
            post.save() # Need to save to database before we can add tags to the post

            import re
            regex = re.compile('\s\s*')
            
            tags_to_add = set(regex.split( request.POST['tags_str'].strip().lower() )) - set([''])
            
            if len(tags_to_add) > 0:
                for tag_name in list(tags_to_add):
                    t = Tag.objects.get_or_create(blog=blog, name=tag_name)
                    
                    # t[1] is True if this was created
                    if t[1]:
                        t[0].save()
                        
                    post.tags.add( t[0] )
                
                post.save()
                
            # End: Save post
            
            request.user.message_set.create(message='Post added successfully')
            
            if continue_edit:      # User clicked "Save and Continue"
                # Go to the edit post page
                return HttpResponseRedirect(reverse('blogs.admin.post_edit', args=(blog.slug, post.slug,)))
                
            else:                 # User clicked "Save as Draft" or "Publish"
                # Return to main blog page
                return HttpResponseRedirect(reverse('blogs.admin.dashboard', args=(blog.slug,)))
            
    else:
        form = AddPostForm()
        
    return render_to_response('blogs/admin/post_edit.html', 
        admin_reponse_direct(blog, request.user, {
            'user': request.user, 
            'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
            'form': form,
            'blog_name': blog.name,
            'blog_slug': blog_slug,
    }), context_instance=RequestContext(request))
    
    
@staff_required
def post_edit(request, blog_slug, post_slug):
        
    blog = get_object_or_404(Blog, pk=blog_slug)
    post = get_object_or_404(Post, blog=blog_slug, slug=post_slug)
        
    # Whether user wants to continue editing the post or not
    continue_edit = not request.POST.get('_continue') == None
        
    # Whether user wants to save draft or publish
    publish = not continue_edit and request.POST.get('_save') == None
    
    form = None
    
    if request.method == 'POST':
        
        # Grab the form
        form = EditPostForm(request.POST)    
        
        # Check if form is valid, if so then save it
        if form.is_valid():
            
            if request.POST.get('byline'):
                byline = request.POST['byline']
            else:
                byline = request.user.username

            # Start: Save post 
            post.title = request.POST['title']
            post.slug = request.POST['slug']
            post.byline = byline
            post.body = clean_body(request.POST['body'])
            post.comments_enabled = request.POST.get('comments_enabled') == 'on'
            post.mod_user = request.user.username
            
            import re
            regex = re.compile('\s\s*')
            
            prev_tags = set( [ tag.name for tag in post.tags.all() ] )
            curr_tags = set( regex.split( request.POST['tags_str'].strip().lower() ) )
            
            tags_to_delete = prev_tags - curr_tags
            tags_to_add = (curr_tags - prev_tags) - set([''])
            
            for tag_name in list(tags_to_add):
                t = Tag.objects.get_or_create(blog=blog, name=tag_name)
                
                # t[1] is True if this was created
                if t[1]:
                    t[0].save()
                    
                post.tags.add( t[0] )
            
            for tag_name in list(tags_to_delete):
                t = Tag.objects.filter(blog=blog, name=tag_name)
                post.tags.remove( t[0] )
                cache.delete('%s__%s' % (blog_slug, tag_name))
                
            from datetime import datetime
            post.mod_date = datetime.now()
            
            if not post.pub_date and publish:
                post.pub_date = post.mod_date
            
            post.save()
            
            # End: Save post
            
            if continue_edit:
                request.user.message_set.create(message='This post has been modified succesfully.')
                
            else:
                back_url = request.POST.get('back')
                
                if back_url == None:
                    # Return to main blog page
                    request.user.message_set.create(message='Post modified successfully.')
                    return HttpResponseRedirect(reverse('blogs.admin.dashboard', args=(blog.slug,)))
                    
                else:
                    # Return to back URL
                    return HttpResponseRedirect(back_url)
    else:
    
        post_data = {
            'title': post.title,
            'slug': post.slug,
            'body': post.body,
            'byline': post.byline,
            'tags_str': " ".join([ tag.name for tag in post.get_tags() ]),
            'comments_enabled': post.comments_enabled,
        }
        form = EditPostForm(post_data)

    return render_to_response('blogs/admin/post_edit.html', 
        admin_reponse_direct(blog, request.user, {
            'user': request.user, 
            'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
            'form': form,
            'post': post,
            'back': request.GET.get('back'),    # URL to redirect to after edit
    }), context_instance=RequestContext(request))
    
    
@staff_required
def post_delete(request, blog_slug, post_slug):

    blog = get_object_or_404(Blog, pk=blog_slug)
    post = get_object_or_404(Post, blog=blog_slug, slug=post_slug)
    
    if request.method == 'POST':
        post.tags.clear()
        post.delete()
        request.user.message_set.create(message='Post deleted successfully.')
        return HttpResponseRedirect(reverse('blogs.admin.dashboard', args=(blog.slug,)))
        
    else:
        return render_to_response('blogs/admin/post_delete.html',
            admin_reponse_direct(blog, request.user, {
                'user': request.user, 
                'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
                'post_slug': post_slug,
        }), context_instance=RequestContext(request))

@staff_required
def blog_details(request, blog_slug):

    blog = get_object_or_404(Blog, pk=blog_slug)
    
    if request.method == 'POST':
        
        # Whether user wants to continue editing the post or not
        continue_edit = not request.POST.get('_continue') == None
        
        # Grab the form
        form = BlogDetailsForm(request.POST)    
        
        # Check if form is valid, if so then save it
        if form.is_valid():
            blog.name = request.POST['name']
            blog.description = request.POST['description']
            blog.at_most = request.POST['at_most']
            blog.date_format = request.POST['date_format']
            blog.time_format = request.POST['time_format']
            
            blog.save()
            
            request.user.message_set.create(message='Blog details have been modified successfully')
        
            if not continue_edit:
                return HttpResponseRedirect(reverse('blogs.admin.dashboard', args=(blog.slug,)))
        
    else:
    
        blog_data = {
            'name': blog.name,
            'slug': blog.slug,
            'description': blog.description,
            'users': blog.users,
            'at_most': blog.at_most,
            'date_format': blog.date_format,
            'time_format': blog.time_format,
        }
        
        form = BlogDetailsForm(blog_data)

    return render_to_response('blogs/admin/blog_details.html', admin_reponse_direct(blog, request.user, {
        'user': request.user, 
        'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
        'form': form,
    }), context_instance=RequestContext(request))


@staff_required
def comments(request, blog_slug):
    blog = get_object_or_404(Blog, pk=blog_slug)
    
    page_num = request.GET.get('page')
    
    if page_num == None:
        page_num = 1
    else:
        page_num = int(page_num)
    
    comments = Comment.objects.filter(blog=blog.pk).order_by('-date')
    
    page_num = int(page_num)
    
    paginator = ObjectPaginator(comments, settings.BLOG_ADMIN_NUM_ITEMS)    

    return render_to_response('blogs/admin/comments.html', admin_reponse_direct(blog, request.user, {
        'user': request.user, 
        'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
        'has_previous_page': paginator.has_previous_page(page_num-1),
        'has_next_page': paginator.has_next_page(page_num-1),
        'prev_page': page_num-1,
        'next_page': page_num+1,
        'comments': paginator.get_page(page_num-1),
    }), context_instance=RequestContext(request))

@staff_required
def comment_edit(request, blog_slug, comment_id):

        
    blog = get_object_or_404(Blog, pk=blog_slug)
    comment = get_object_or_404(Comment, pk=comment_id)
        
    # Whether user wants to continue editing the post or not
    continue_edit = not request.POST.get('_continue') == None
        
    # Whether user wants to save draft or publish
    #publish = not continue_edit and request.POST.get('_save') == None
    
    form = CommentForm(request.POST)
    
    if request.method == 'POST':
        
        # Check if form is valid, if so then save it
        if form.is_valid():
            
            # Start: Save comment 
            comment.name = request.POST['name']
            comment.email = request.POST['email']
            comment.message = request.POST['message']
                        
            comment.save()
            
            # End: Save comment
            
            if continue_edit:      # User clicked "Save and Continue"
                request.user.message_set.create(message='This comment has been modified successfully.')
            else:
                request.user.message_set.create(message='Comment modified successfully.')
                return comments(request, blog_slug)

    else:
        comment_data = {
            'name': comment.name,
            'email': comment.email,
            'message': comment.message,
        }
        form = CommentForm(comment_data)

    return render_to_response('blogs/admin/comment_edit.html', admin_reponse_direct(blog, request.user, {
        'user': request.user, 
        'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
        'form': form,
    }), context_instance=RequestContext(request))
    
    
@staff_required
def comment_delete(request, blog_slug, comment_id):
    
    blog = get_object_or_404(Blog, pk=blog_slug)
    comment = get_object_or_404(Comment, pk=comment_id)
    
    if request.method == 'POST':
        comment.delete()
        request.user.message_set.create(message='Comment deleted successfully.' )
        return HttpResponseRedirect(reverse('blogs.admin.comments', args=(blog_slug,)))
        
    else:
        return render_to_response('blogs/admin/comment_delete.html', admin_reponse_direct(blog, request.user, {
            'user': request.user, 
            'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
            'comment_id': comment_id,
        }), context_instance=RequestContext(request))
    
        
@staff_required
def comment_mass_delete(request, blog_slug):
    
    blog = get_object_or_404(Blog, pk=blog_slug)
    
    if request.method == 'POST':
        comment_ids = request.POST.getlist('selected_comment')
        
        for comment_id in comment_ids:
            comment = get_object_or_404(Comment, pk=comment_id)
            comment.delete()
            
        request.user.message_set.create(message='Comments deleted successfully.' )
    
    return HttpResponseRedirect(reverse('blogs.admin.comments', args=(blog_slug,)))
    
    
@staff_required
def ban_ip(request, blog_slug):

    blog = get_object_or_404(Blog, pk=blog_slug)
    
    if request.method == 'POST':
        back_url = request.POST.get('back_url')
        ip_to_ban = request.POST.get('ip')
        banned = Banned(blog=blog,ip=ip_to_ban)
        banned.save()
        
        if not request.POST.get('_delete') == None:
            comments_to_delete = Comment.objects.filter(ip=ip_to_ban)
            
            for comment in comments_to_delete:
                comment.delete()
            
        request.user.message_set.create(message='IP %s banned successfully.' % ip_to_ban )        
        
    else:
        ip_to_ban = request.GET.get('ip')
        
        if ip_to_ban:
            return render_to_response('blogs/admin/ban_ip.html', admin_reponse_direct(blog, request.user, {
                'user': request.user, 
                'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
                'ip_to_ban': ip_to_ban,
            }), context_instance=RequestContext(request))
        
    return HttpResponseRedirect(reverse('blogs.admin.comments', args=(blog_slug,)))
        
@staff_required
def banned_ips(request, blog_slug):

    blog = get_object_or_404(Blog, pk=blog_slug)
    
    ips = Banned.objects.filter(blog=blog_slug)

    return render_to_response('blogs/admin/banned_ips.html', admin_reponse_direct(blog, request.user, {
        'user': request.user, 
        'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
        'ips': ips,
    }), context_instance=RequestContext(request))
    
    
@staff_required
def unban_ip(request, blog_slug):

    blog = get_object_or_404(Blog, pk=blog_slug)
    
    if request.method == 'GET' and request.GET.get('ip'):
        ip_to_unban = request.GET.get('ip')
        banned = get_object_or_404(Banned, blog=blog, ip=ip_to_unban)
        banned.delete()
            
        request.user.message_set.create(message='IP %s unbanned successfully.' % ip_to_unban)

        
    return HttpResponseRedirect(reverse('blogs.admin.banned_ips', args=(blog_slug,)))
        

@staff_required
def blogroll(request, blog_slug):
    blog = get_object_or_404(Blog, pk=blog_slug)
    
    if request.method == 'POST':
        link_ids = request.POST.getlist('selected_link')
        
        for link_id in link_ids:
            link = get_object_or_404(Link, pk=link_id)
            link.delete()
        
    links = Link.objects.filter(blog=blog.pk).order_by('name')
    
    return render_to_response('blogs/admin/blogroll.html', admin_reponse_direct(blog, request.user, {
        'user': request.user, 
        'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
        'links': links,
    }), context_instance=RequestContext(request))

@staff_required
def link_add(request, blog_slug):
    
    blog = get_object_or_404(Blog, pk=blog_slug)

    if request.method == 'POST':
        
        # Grab the form
        form = LinkForm(request.POST)
        
        # Check if form is valid, if so then save it
        if form.is_valid():
            
            # Start: Save link
            
            from datetime import datetime
            
            link = Link(
                blog = blog,
                name = request.POST['name'],
                url = request.POST['url'],
                tooltip = request.POST['tooltip'],
                active = request.POST.get('active') == "on"
            )
                
            link.save() # Need to save to database before we can add tags to the post

            # End: Save link
            
            request.user.message_set.create(message='Link added successfully')
            
            return HttpResponseRedirect(reverse('blogs.admin.blogroll', args=(blog.slug,)))
            
    else:
        form = LinkForm()
    
    return render_to_response('blogs/admin/link_edit.html', 
        admin_reponse_direct(blog, request.user, {
            'user': request.user, 
            'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
            'form': form,
            'blog_name': blog.name,
            'blog_slug': blog_slug,
    }), context_instance=RequestContext(request))

@staff_required
def link_edit(request, blog_slug, link_id):
        
    blog = get_object_or_404(Blog, pk=blog_slug)
    link = get_object_or_404(Link, pk=link_id)
        
    # Whether user wants to continue editing the post or not
    continue_edit = not request.POST.get('_continue') == None
        
    # Whether user wants to save draft or publish
    #publish = not continue_edit and request.POST.get('_save') == None
    
    form = LinkForm(request.POST)
    
    if request.method == 'POST':
        
        # Check if form is valid, if so then save it
        if form.is_valid():
            
            # Start: Save link 
            link.name = request.POST['name']
            link.url = request.POST['url']
            link.tooltip = request.POST['tooltip']
            link.active = request.POST.get('active') == "on"
                        
            link.save()
            
            # End: Save link
            
            if continue_edit:      # User clicked "Save and Continue"
                request.user.message_set.create(message='This link has been modified successfully.')
            else:
                request.user.message_set.create(message='Link modified successfully.')
                return blogroll(request, blog_slug)

    else:
        link_data = {
            'name': link.name,
            'url': link.url,
            'tooltip': link.tooltip,
            'active': link.active,
        }
        form = LinkForm(link_data)

    return render_to_response('blogs/admin/link_edit.html', admin_reponse_direct(blog, request.user, {
        'user': request.user, 
        'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
        'form': form,
    }), context_instance=RequestContext(request))
    
    
@staff_required
def link_delete(request, blog_slug, link_id):
    
    blog = get_object_or_404(Blog, pk=blog_slug)
    link = get_object_or_404(Link, pk=link_id)
    
    if request.method == 'POST':
        link.delete()
        request.user.message_set.create(message='Link deleted successfully.' )
        return HttpResponseRedirect(reverse('blogs.admin.blogroll', args=(blog_slug,)))
        
    else:
        return render_to_response('blogs/admin/link_delete.html', admin_reponse_direct(blog, request.user, {
            'user': request.user, 
            'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
            'link_id': link_id,
        }), context_instance=RequestContext(request))
    
        
@staff_required
def link_mass_delete(request, blog_slug):
    
    blog = get_object_or_404(Blog, pk=blog_slug)
    
    if request.method == 'POST':
        link_ids = request.POST.getlist('selected_link')
        
        for link_id in link_ids:
            link = get_object_or_404(Link, pk=link_id)
            link.delete()
            
        request.user.message_set.create(message='Links deleted successfully.' )
    
    return HttpResponseRedirect(reverse('blogs.admin.blogroll', args=(blog_slug,)))
    
        
@staff_required
def upload_image(request, blog_slug):

    blog = get_object_or_404(Blog, pk=blog_slug)

    if request.method == 'GET' and request.GET.get('tab') == 'browse':
        images = Image.objects.filter(blog=blog_slug)
        
        return render_to_response('blogs/admin/upload_image.html', 
            admin_reponse_direct(blog, request.user, {
                'user': request.user, 
                'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
                'curr_tab': 'browse',
                'images': images,
                'blog_name': blog.name,
                'blog_slug': blog_slug,
                'is_popup': True,
        }), context_instance=RequestContext(request))

    elif request.method == 'POST':
        
        # Grab the form
        form = ImageForm(request.POST, request.FILES)
        
        # Check if form is valid, if so then save it
        if form.is_valid():
        
            image = form.save(commit=False)
            image.blog = blog
                
            image.save()
            
            request.user.message_set.create(message='Image uploaded successfully')
            
            return HttpResponseRedirect('%s?tab=browse' % reverse('blogs.admin.upload_image', args=(blog.slug,)))
            
    else:
        form = ImageForm()
        
    return render_to_response('blogs/admin/upload_image.html', 
        admin_reponse_direct(blog, request.user, {
            'user': request.user, 
            'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
            'curr_tab': 'upload',
            'form': form,
            'blog_name': blog.name,
            'blog_slug': blog_slug,
            'is_popup': True,
    }), context_instance=RequestContext(request))
